#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>

#include "Cond.hpp"

namespace BlockQueueModle
{
    using namespace MpthreayMutex;
    using namespace MyCond;
    template <class T>
    class BlockQueue
    {
    private:
        bool IsFull() { return _bq.size() == _cap; };
        bool IsEmpty() { return _bq.empty(); };

    public:
        BlockQueue(int cap) : _cap(cap)
        {
        }
        void Equeue(T &data) // 生产者生产商品
        {
            _mtx.Lock();
            while (IsFull())
            {
                std::cout << "生产者进入等待队列" << std::endl;
                wait_proc++;
                product_cond.wait(_mtx); // 生产者被唤醒之后只有当重新拿到锁之后才会继续向下执行
                wait_proc--;
                std::cout << "生产者被唤醒" << std::endl;
            }
            // 生产者被唤醒 | 数据插入
            _bq.push(data); // 一定有数据，唤醒消费者
            std::cout << "货物总数: " << _bq.size() << std::endl;
            // 货物总数远远大于限定的，为什么呢?
            // 当我们货物满的时候，生产者一旦生产满，就会去等待，但是原来的队列当中也有生产者被唤醒了
            // 只要第一个生产者销毁锁后，就会重新执行锁，所以总大小要大于设置的大小
            if (wait_cus > 0)
            {
                customer_cond.NotifyAll();
                // pthread_cond_signal(&customer_cond);
            }
            _mtx.Unlock();
        }
        void Pop(T *data) // 消费者消费商品
        {
            _mtx.Lock();
            while (IsEmpty())
            {
                std::cout << "消费者进入等待队列" << std::endl;
                wait_cus++;
                customer_cond.wait(_mtx); // 1. 在临界去中被激活必须要加锁
                                      // 消费者被唤醒之后只有当重新拿到锁之后才会继续向下执行
                wait_cus--;
                std::cout << "消费者被唤醒" << std::endl;
            }
            // 队列为空 | 消费者被唤醒
            *data = _bq.front();
            _bq.pop(); // 一定买走了东西
            std::cout << "消费者买完还剩下: " << _bq.size() << std::endl;
            if (wait_proc > 0)
            {
                // pthread_cond_signal(&product_cond); //唤醒生产者继续生产
                product_cond.NotifyAll();
            }
            _mtx.Unlock();
        }
        ~BlockQueue()
        {
        }

    private:
        int _cap;
        std::queue<T> _bq;
        Mutex _mtx;
        Cond product_cond;
        Cond customer_cond;
        int wait_proc = 0;
        int wait_cus = 0;
    };
}
// version 1
//  namespace BlockQueueModle
//  {
//      using namespace MpthreayMutex;
//      template <class T>
//      class BlockQueue
//      {
//      private:
//          bool IsFull() { return _bq.size() == _cap; };
//          bool IsEmpty() { return _bq.empty(); };
//      public:

//         BlockQueue(int cap) : _cap(cap)
//         {
//             pthread_mutex_init(&_mtx, nullptr);
//             pthread_cond_init(&product_cond, nullptr);
//             pthread_cond_init(&customer_cond, nullptr);
//         }
//         void Equeue(T &data) // 生产者生产商品
//         {
//             pthread_mutex_lock(&_mtx);
//             while (IsFull())
//             {
//                 std::cout << "生产者进入等待队列" << std::endl;
//                 wait_proc++;
//                 pthread_cond_wait(&product_cond, &_mtx);//生产者被唤醒之后只有当重新拿到锁之后才会继续向下执行
//                 wait_proc--;
//                 std::cout << "生产者被唤醒" << std::endl;
//             }
//             //生产者被唤醒 | 数据插入
//             _bq.push(data);  //一定有数据，唤醒消费者
//             std::cout << "货物总数: " << _bq.size() << std::endl;
//             //货物总数远远大于限定的，为什么呢?
//             //当我们货物满的时候，生产者一旦生产满，就会去等待，但是原来的队列当中也有生产者被唤醒了
//             //只要第一个生产者销毁锁后，就会重新执行锁，所以总大小要大于设置的大小
//             if (wait_cus > 0)
//             {
//                 pthread_cond_broadcast(&customer_cond);
//                 // pthread_cond_signal(&customer_cond);
//             }
//             pthread_mutex_unlock(&_mtx);
//         }
//         void Pop(T *data) // 消费者消费商品
//         {
//             pthread_mutex_lock(&_mtx);
//             while (IsEmpty())
//             {
//                 std::cout << "消费者进入等待队列" << std::endl;
//                 wait_cus++;
//                 pthread_cond_wait(&customer_cond, &_mtx); //1. 在临界去中被激活必须要加锁
//                  //消费者被唤醒之后只有当重新拿到锁之后才会继续向下执行
//                 wait_cus--;
//                 std::cout << "消费者被唤醒" << std::endl;
//             }
//             //队列为空 | 消费者被唤醒
//             *data = _bq.front();
//             _bq.pop(); //一定买走了东西
//             std::cout << "消费者买完还剩下: " << _bq.size() << std::endl;
//             if(wait_proc > 0)
//             {
//                 // pthread_cond_signal(&product_cond); //唤醒生产者继续生产
//                 pthread_cond_broadcast(&product_cond);
//             }
//             pthread_mutex_unlock(&_mtx);
//         }
//         ~BlockQueue()
//         {
//             pthread_mutex_destroy(&_mtx);
//             pthread_cond_destroy(&product_cond);
//             pthread_cond_destroy(&customer_cond);
//         }

//     private:
//         int _cap;
//         std::queue<T> _bq;
//         Mutex _mtx;
//         pthread_cond_t product_cond;
//         pthread_cond_t customer_cond;
//         int wait_proc = 0;
//         int wait_cus = 0;
//     };
// }
